package com.jzlin.funhub.framework.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具.
 *
 * @author linjz
 * @since 2025/5/28
 */
@Slf4j
@Component
public class RedisUtil {
    private static final String CACHE_PREFIX_KEY = "prefix";
    private static final int MAXLENGTH = 20;

    @Value("${cache.nullValueExpire:10}")
    private long nullValueExpire;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public long getNullValueExpire() {
        return this.nullValueExpire;
    }

    private String buildRedisKey(String... keys) {
        if (keys == null) {
            throw new IllegalArgumentException("keys 不能为空.");
        } else {
            int length = keys.length;
            if (length == 0) {
                throw new IllegalArgumentException("keys 不能为空.");
            } else if (length == 1) {
                return keys[0];
            } else {
                StringBuilder builder = new StringBuilder();

                for(int i = 0; i < length; ++i) {
                    builder.append(keys[i]);
                    if (i != length - 1) {
                        builder.append(":");
                    }
                }

                return builder.toString();
            }
        }
    }

    private boolean savePrefix2Set(String prefix, String key, long score) {
        if (StringUtils.isEmpty(prefix)) {
            throw new IllegalArgumentException("prefix 不能为空.");
        } else if (StringUtils.isEmpty(key)) {
            throw new IllegalArgumentException("key 不能为空.");
        } else {
            String redisKey = this.buildRedisKey(prefix, key);
            Boolean isAddSuccess = this.redisTemplate.opsForZSet().add(this.buildRedisKey("prefix", prefix), redisKey, score >= 0L ? (double)(System.currentTimeMillis() + score) : -1.0);
            return isAddSuccess != null && isAddSuccess;
        }
    }

    public boolean savePrefix(String prefix, String key, Object value) {
        if (!this.savePrefix2Set(prefix, key, -1L)) {
            return false;
        } else {
            this.redisTemplate.opsForValue().set(this.buildRedisKey(prefix, key), value == null ? "" : value.toString());
            return true;
        }
    }

    public boolean savePrefix(String prefix, String key, Object value, Long ttl) {
        if (ttl != null && ttl > 0L) {
            if (!this.savePrefix2Set(prefix, key, ttl)) {
                return false;
            } else {
                this.redisTemplate.opsForValue().set(this.buildRedisKey(prefix, key), value == null ? "" : value.toString(), ttl, TimeUnit.MILLISECONDS);
                return true;
            }
        } else {
            return this.savePrefix(prefix, key, value);
        }
    }

    public boolean savePrefix(String prefix, String key, Map<String, Object> value) {
        if (value != null && !value.isEmpty()) {
            if (!this.savePrefix2Set(prefix, key, -1L)) {
                return false;
            } else {
                this.redisTemplate.opsForHash().putAll(this.buildRedisKey(prefix, key), value);
                return true;
            }
        } else {
            throw new IllegalArgumentException("value 不能为空.");
        }
    }

    public boolean savePrefix(String prefix, String key, Map<String, Object> value, Long ttl) {
        if (ttl != null && ttl > 0L) {
            if (!this.savePrefix2Set(prefix, key, ttl)) {
                return false;
            } else {
                String finalKey = this.buildRedisKey(prefix, key);
                this.redisTemplate.opsForHash().putAll(finalKey, value);
                Boolean isExpireSuccess = this.redisTemplate.expire(finalKey, ttl, TimeUnit.MILLISECONDS);
                return isExpireSuccess != null && isExpireSuccess;
            }
        } else {
            return this.savePrefix(prefix, key, value);
        }
    }

    public int removePrefix(String prefix) {
        if (StringUtils.isEmpty(prefix)) {
            throw new IllegalArgumentException("prefix 不能为空.");
        } else {
            log.info("按前缀删除，prefix：{}", prefix);
            long maxEndIndex = Long.MAX_VALUE;
            long startIndex = -1L;
            long length = 100L;
            String zKey = this.buildRedisKey("prefix", prefix);
            int removeCount = 0;

            for(Set<Object> set = this.redisTemplate.opsForZSet().rangeByScore(zKey, (double)startIndex, (double)maxEndIndex, 0L, length); set != null && !set.isEmpty(); set = this.redisTemplate.opsForZSet().rangeByScore(zKey, (double)startIndex, (double)maxEndIndex, 0L, length)) {
                Set<String> keys = new HashSet(set.size());
                Iterator var14 = set.iterator();

                while(var14.hasNext()) {
                    Object obj = var14.next();
                    if (obj != null) {
                        String key = obj.toString();
                        keys.add(key);
                        this.redisTemplate.delete(key);
                        ++removeCount;
                    }
                }

                if (!keys.isEmpty()) {
                    String[] keysArray = new String[keys.size()];
                    keysArray = (String[])keys.toArray(keysArray);
                    this.redisTemplate.opsForZSet().remove(zKey, (Object[])keysArray);
                    log.info("按前缀删除，prefix：{}，影响到的数据条数：{}，数据项为：{}", new Object[]{prefix, set.size(), set.toString()});
                }
            }

            log.info("按前缀删除，prefix：{}，影响到的总数据条数：{}", prefix, removeCount);
            return removeCount;
        }
    }

    public void remove(String... keys) {
        String[] var2 = keys;
        int var3 = keys.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String key = var2[var4];
            this.remove(key);
        }

    }

    public boolean remove(String key) {
        if (StringUtils.isEmpty(key)) {
            return false;
        } else {
            try {
                this.redisTemplate.delete(key);
                return true;
            } catch (Exception var3) {
                log.error(var3.getMessage(), var3);
                return false;
            }
        }
    }

    public boolean exists(String key) {
        return this.redisTemplate.hasKey(key);
    }

    public Object get(String key) {
        Object result;
        try {
            result = this.redisTemplate.opsForValue().get(key);
        } catch (Exception var4) {
            log.error(var4.getMessage(), var4);
            return null;
        }

        return result;
    }

    public Object get(String key, Object defaultValue) {
        Object result;
        try {
            result = this.redisTemplate.opsForValue().get(key);
        } catch (Exception var5) {
            log.error(var5.getMessage(), var5);
            return defaultValue;
        }

        return Optional.ofNullable(result).orElse(defaultValue);
    }


    public boolean set(String key, List<Object> value) {
        if (value != null && !value.isEmpty()) {
            try {
                Long ret = this.redisTemplate.opsForList().rightPushAll(key, value);
                return ret != null && ret > 0L;
            } catch (Exception var4) {
                log.error(var4.getMessage(), var4);
                return false;
            }
        } else {
            return false;
        }
    }

    public boolean setNX(String key, Object value) {
        try {
            boolean isSuccess = this.redisTemplate.opsForValue().setIfAbsent(key, value);
            return isSuccess;
        } catch (Exception var5) {
            log.error(var5.getMessage(), var5);
            return false;
        }
    }

    public boolean set(String key, Object value, Long expireTime) {
        try {
            this.redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
            return true;
        } catch (Exception var5) {
            log.error(var5.getMessage(), var5);
            return false;
        }
    }

    public void setNull(String key) {
        if (!StringUtils.isEmpty(key)) {
            this.redisTemplate.opsForValue().set(key, "", this.nullValueExpire, TimeUnit.MINUTES);
        }

    }

    public void set(String key, Object value) {
        if (!StringUtils.isEmpty(key)) {
            this.redisTemplate.opsForValue().set(key, value);
        }

    }

    public void set(String key, Object value, long time) {
        if (!StringUtils.isEmpty(key)) {
            this.set(key, value, time, TimeUnit.SECONDS);
        }

    }

    public void set(String key, Object value, long time, TimeUnit timeUnit) {
        if (!StringUtils.isEmpty(key)) {
            this.redisTemplate.opsForValue().set(key, value, time, timeUnit);
        }

    }

    public void delete(String key) {
        if (!StringUtils.isEmpty(key)) {
            this.redisTemplate.delete(key);
        }

    }

    public void delete(Set<String> keys) {
        if (keys != null && !keys.isEmpty()) {
            this.redisTemplate.delete(keys);
        }

    }

    public void deleteByPattern(String pattern) {
        Set<String> keys = this.keys(pattern);
        if (keys.size() > 0) {
            this.redisTemplate.delete(keys);
        }

    }

    public Set<String> keys(String key) {
        return (Set)(!StringUtils.isEmpty(key) ? this.redisTemplate.keys(key) : new HashSet());
    }

    public long getExpire(String key) {
        return !StringUtils.isEmpty(key) ? this.redisTemplate.getExpire(key) : 0L;
    }

    public void expire(String key, long time, TimeUnit timeUnit) {
        if (!StringUtils.isEmpty(key)) {
            this.redisTemplate.expire(key, time, timeUnit);
        }

    }

    public boolean expireAt(String key, Date date) {
        return !StringUtils.isEmpty(key) && null != date ? this.redisTemplate.expireAt(key, date) : false;
    }

    public void multiSet(Map<String, String> maps) {
        this.redisTemplate.opsForValue().multiSet(maps);
    }

    public void multiSetObject(Map<String, Object> maps) {
        if (maps != null && !maps.isEmpty()) {
            this.redisTemplate.opsForValue().multiSet(maps);
        }

    }

    public Long incr(String key) {
        return this.redisTemplate.opsForValue().increment(key, 1L);
    }

    public Long incr(String key, Long incrValue) {
        return this.redisTemplate.opsForValue().increment(key, incrValue);
    }

    public Long leftPush(String key, Object val) {
        return this.redisTemplate.opsForList().leftPush(key, val);
    }

    public Long leftPushAll(String key, Object[] val) {
        return this.redisTemplate.opsForList().leftPushAll(key, val);
    }

    public Long leftPushAll(String key, List<Object> list) {
        return this.redisTemplate.opsForList().leftPushAll(key, list);
    }

    public Long leftPush(String key, Object val, int maxSize) {
        Long size = this.redisTemplate.opsForList().leftPush(key, val);
        if (size == null) {
            return null;
        } else {
            if (size >= (long)maxSize) {
                this.redisTemplate.opsForList().leftPop(key);
            }

            return size;
        }
    }

    public List<Object> getListRange(String key, long min, long max) {
        return this.redisTemplate.opsForList().range(key, min, max);
    }

    public Object hGet(String key, String field) {
        return this.redisTemplate.opsForHash().get(key, field);
    }

    public Map<Object, Object> hGetAll(String key) {
        return this.redisTemplate.opsForHash().entries(key);
    }

    public List<Object> hMultiGet(String key, Collection<Object> fields) {
        return this.redisTemplate.opsForHash().multiGet(key, fields);
    }

    public void hPut(String key, String hashKey, Object value) {
        this.redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public void hPutAll(String key, Map<String, String> maps) {
        this.redisTemplate.opsForHash().putAll(key, maps);
    }

    public Long hDelete(String key, Object... fields) {
        return this.redisTemplate.opsForHash().delete(key, fields);
    }

    public boolean hExists(String key, String field) {
        return this.redisTemplate.opsForHash().hasKey(key, field);
    }

    public boolean addSet(String key, Object value) {
        if (value != null && !value.toString().isEmpty()) {
            try {
                Long ret = this.redisTemplate.opsForSet().add(key, new Object[]{value});
                return ret > 0L;
            } catch (Exception var4) {
                log.error(var4.getMessage(), var4);
                return false;
            }
        } else {
            return false;
        }
    }

    public boolean removeSet(String key, Object value) {
        if (value != null && !value.toString().isEmpty()) {
            try {
                Long ret = this.redisTemplate.opsForSet().remove(key, new Object[]{value});
                return ret > 0L;
            } catch (Exception var4) {
                log.error(var4.getMessage(), var4);
                return false;
            }
        } else {
            return false;
        }
    }

    public Set<Object> getAllSet(String key) {
        try {
            return this.redisTemplate.opsForSet().members(key);
        } catch (Exception var3) {
            log.error(var3.getMessage(), var3);
            return new HashSet();
        }
    }

    public Boolean addZSet(String key, Object value, double score) {
        if (value != null && !value.toString().isEmpty()) {
            try {
                return this.redisTemplate.opsForZSet().add(key, value, score);
            } catch (Exception var6) {
                log.error(var6.getMessage(), var6);
                return false;
            }
        } else {
            return false;
        }
    }

    public Double incrZSet(String key, Object value, double delta) {
        return this.redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    public Double incrZSet(String key, Object value) {
        return this.redisTemplate.opsForZSet().incrementScore(key, value, 1.0);
    }

    public Set<Object> zSetReverseRangeByScore(String key, double min, double max) {
        return this.redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }
}
